/*
Copyright (c) 2025 HeZongLun
NahidaProject is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

#include "ODBC.h"
#include <sstream>

int NahidaProject::ODBC::Connect(SQLCHAR* datasource, SQLCHAR* username, SQLCHAR* password) {
	SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
	SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
	ret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
	ret = SQLConnect(this->hdbc, datasource, SQL_NTS, username, SQL_NTS, password, SQL_NTS);

	return ret;
}

void NahidaProject::ODBC::RegisterTransaction(std::string transactionName, std::function<void()> transactionFunction){
	this->transactionList.insert(std::pair<std::string, std::function<void()>>(transactionName, transactionFunction));
}

void NahidaProject::ODBC::InvokeTransaction(std::string transactionName){
	ExecuteSQLWithoutResult((SQLCHAR*)"BEGIN TRANSACTION;");
	this->transactionList[transactionName]();
	ExecuteSQLWithoutResult((SQLCHAR*)"COMMITE TRANSACTION;");
}

void NahidaProject::ODBC::UndoTransaction(){
	ExecuteSQLWithoutResult((SQLCHAR*)"ROLLBACK TRANSACTION;");
}

void NahidaProject::ODBC::DeleteTransaction(std::string transactionName){
	this->transactionList.erase(transactionName);
}

int NahidaProject::ODBC::ExecuteSQLWithoutResult(SQLCHAR* sqlStatement) {
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	ret = SQLExecDirect(hstmt, sqlStatement, SQL_NTS);
	SQLCancel(hstmt);
	return ret;
}

std::vector<std::vector<std::string>> NahidaProject::ODBC::ExecuteSQLWithResult(SQLCHAR* sqlStatement) {
	std::vector<std::vector<std::string>> results;
	
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	ret = SQLExecDirect(hstmt, sqlStatement, SQL_NTS);
	if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO){
		return { };
	}

	SQLCHAR midData[MAXCHAR] = { 0 };
	SQLLEN midDataLength = 0;
	SQLSMALLINT columnCount = 0;
	SQLNumResultCols(hstmt, &columnCount);

	int line = 0;
	while (SQLFetch(hstmt) != SQL_NO_DATA_FOUND) {
		std::vector<std::string> result;
		++line;
		for (int col = 1; col <= columnCount; ++col){
			midData[0] = 0;
			ret = SQLGetData(hstmt, col, SQL_C_CHAR, midData, MAXCHAR, &midDataLength);
			if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO){
				result.push_back((const char*)(char*)midData);
			}
		}
		results.push_back(result);
	}
	SQLCancel(hstmt);
	return results;
}

void NahidaProject::ODBC::Close() {
	this->transactionList.clear();
	SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
	SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
